package centralRepository;

import java.util.ArrayList;
import java.util.Scanner;

import org.omg.CORBA.ORB;
import org.omg.CORBA.ORBPackage.InvalidName;
import org.omg.CosNaming.Binding;
import org.omg.CosNaming.BindingIteratorHolder;
import org.omg.CosNaming.BindingListHolder;
import org.omg.CosNaming.BindingType;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.CosNaming.NamingContextPackage.CannotProceed;
import org.omg.CosNaming.NamingContextPackage.NotFound;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAManagerPackage.AdapterInactive;
import org.omg.PortableServer.POAPackage.ServantNotActive;
import org.omg.PortableServer.POAPackage.WrongPolicy;

import server.Actions;
import server.ActionsHelper;
import server.ActionsImpl;

import java.net.UnknownHostException;

import javax.swing.JOptionPane;

public class CentralRepository {

	private static ORB orb;
	private static NamingContextExt ncRef;
	private static ArrayList<ServerObject> processes;
	private static ArrayList<ServerObjectImpl> list = new ArrayList<ServerObjectImpl>();
	private static String hostname;
	private static int portNum = 1050;
	private static Object processesLock=new Object();

	//singleton
	private CentralRepository() {

	}

	public static ServerObjectImpl getLeader(String server) {
		synchronized(processesLock) {
			getAllProcesses();
			for (int i=0;i<processes.size();++i) {
				if (processes.get(i).isLeader()&&processes.get(i).getStation().equals(server))
					return new ServerObjectImpl(processes.get(i));
			}
		}
		return null;
	}

	public static void startClientServers() {
		if (ncRef==null)
			init();
		String[] serverNames={"SPVM", "SPL", "SPB"};
		int[] portNums={4000, 4001, 4002};
		for (int i=0; i<serverNames.length; ++i) {
			// get reference to rootpoa & activate the POAManager
			POA rootpoa;
			try {
				rootpoa = (POA)orb.resolve_initial_references("RootPOA");
				rootpoa.the_POAManager().activate();

				// create servant and register it with the ORB
				ActionsImpl actionsImpl = new ActionsImpl();

				// get object reference from the servant
				org.omg.CORBA.Object ref = rootpoa.servant_to_reference(actionsImpl);
				// and cast the reference to a CORBA reference
				Actions href = ActionsHelper.narrow(ref);

				// get the root naming context
				// NameService invokes the transient name service
				org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");

				// Use NamingContextExt, which is part of the
				// Interoperable Naming Service (INS) specification.
				NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

				// bind the Object Reference in Naming
				NameComponent path[] = ncRef.to_name(serverNames[i]);
				ncRef.rebind(path, href);
				System.out.println (serverNames[i]+" ready and waiting ...");

			} catch (InvalidName e) {
				e.printStackTrace();
			} catch (AdapterInactive e) {
				e.printStackTrace();
			} catch (ServantNotActive e) {
				e.printStackTrace();
			} catch (WrongPolicy e) {
				e.printStackTrace();
			} catch (org.omg.CosNaming.NamingContextPackage.InvalidName e) {
				e.printStackTrace();
			} catch (NotFound e) {
				e.printStackTrace();
			} catch (CannotProceed e) {
				e.printStackTrace();
			}

		}
	}

	private static void clearBindings() {
		if (ncRef==null)
			init();
		BindingListHolder bl = new BindingListHolder();
		BindingIteratorHolder blIt= new BindingIteratorHolder();
		ncRef.list(1000, bl, blIt);
		Binding bindings[] = bl.value;

		for (int i=0; i < bindings.length; i++) {
			try {
				System.out.println("TRYING TO UNBIND : " + bindings[i].binding_name);
				ncRef.unbind(bindings[i].binding_name);
			} catch (Exception e) {

			}
		}
	}

	private static void init() {
		try {
			System.out.println("My host name: "+java.net.InetAddress.getLocalHost().getHostName());

			hostname = JOptionPane.showInputDialog("Hostname: ");

			String[] args = new String[4]; 
			args[0] = "-ORBInitialPort"; 
			args[1] = ""+portNum; 
			args[2] = "-ORBInitialHost"; 
			args[3] = hostname; 

			orb = ORB.init(args,null); 
			try {
				ncRef = NamingContextExtHelper.narrow(orb.resolve_initial_references("NameService"));
			} catch (InvalidName e) {
				e.printStackTrace();
			} 


		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static ArrayList<ServerObjectImpl> getOtherRunningReplicasInList(ServerObjectImpl server) {
		ArrayList<ServerObjectImpl> sos = new ArrayList<ServerObjectImpl>();
		for (int i=0;i<list.size();++i)
			if (list.get(i).getStatus().equals("RUNNING")&&list.get(i).getStation().equals(server.getStation())&&!list.get(i).getName().equals(server.getName()))
				sos.add(list.get(i));
		return sos;
	}

	public static ArrayList<ServerObjectImpl> getWaitingProcessesInList(ServerObjectImpl server) {
		ArrayList<ServerObjectImpl> sos = new ArrayList<ServerObjectImpl>();
		for (int i=0;i<list.size();++i)
			if (list.get(i).getStatus().equals("WAITING")&&list.get(i).getStation().equals(server.getStation()))
				sos.add(list.get(i));
		return sos;
	}


	public static void getAllProcesses() {
		if (ncRef==null)
			init();
		try {
			synchronized(processesLock) {
				processes = new ArrayList<ServerObject>();

				BindingListHolder bl = new BindingListHolder();
				BindingIteratorHolder blIt= new BindingIteratorHolder();
				ncRef.list(1000, bl, blIt);

				Binding bindings[] = bl.value;

				for (int i=0; i < bindings.length; i++) {

					int lastIx = bindings[i].binding_name.length-1;

					// check to see if this is a naming context
					if (bindings[i].binding_type == BindingType.ncontext) {
						System.out.println( "Context: " + bindings[i].binding_name[lastIx].id);
						//System.out.println("HERE!");
					} else {
						//System.out.println("Object: " + bindings[i].binding_name[lastIx].id);
						//if (ncRef._is_a("")) {
						try {
							NameComponent nc = new NameComponent(bindings[i].binding_name[lastIx].id, "");
							NameComponent path[] = {nc};
								ServerObject so = ServerObjectHelper.narrow(ncRef.resolve(path));
								if (so!=null)
								{
									System.out.println("ADDING PROCESS TO LIST: "+so.getName());
									processes.add(so);
								}
							
						} catch (Exception e) {
							//System.out.println("Caught exception");
						}
					}
				}
			}
		} catch (Exception e) {

		}
	}

	public static ArrayList<ServerObject> getRunningReplicas(ServerObjectImpl server) {
		ArrayList<ServerObject> sos = new ArrayList<ServerObject>();
		synchronized(processesLock) {
			getAllProcesses();
			for (int i=0;i<processes.size();++i)
				if (processes.get(i).getStatus().equals("RUNNING")&&processes.get(i).getStation().equals(server.getStation()))
					sos.add(processes.get(i));
		}
		return sos;
	}

	public static ArrayList<ServerObject> getAllRunning() {
		ArrayList<ServerObject> sos = new ArrayList<ServerObject>();
		synchronized(processesLock) {
			getAllProcesses();
			for (int i=0;i<processes.size();++i)
				if (processes.get(i).getStatus().equals("RUNNING"))
					sos.add(processes.get(i));
		}
		return sos;
	}

	public static ArrayList<ServerObject> getDownReplicas(ServerObjectImpl server) {
		ArrayList<ServerObject> sos = new ArrayList<ServerObject>();
		synchronized(processesLock) {
			getAllProcesses();
			for (int i=0;i<processes.size();++i)
				if (processes.get(i).getStatus().equals("DOWN")&&processes.get(i).getStation().equals(server.getStation()))
					sos.add(processes.get(i));
		}
		return sos;
	}

	public static ArrayList<ServerObject> getAllDown() {
		ArrayList<ServerObject> sos = new ArrayList<ServerObject>();
		synchronized(processesLock) {
			getAllProcesses();

			for (int i=0;i<processes.size();++i)
				if (processes.get(i).getStatus().equals("DOWN"))
					sos.add(processes.get(i));
		}
		return sos;
	}

	public static ArrayList<ServerObject> getOtherRunningReplicas(ServerObjectImpl server) {
		ArrayList<ServerObject> sos = new ArrayList<ServerObject>();
		synchronized(processesLock) {getAllProcesses();

		for (int i=0;i<processes.size();++i)
			if (processes.get(i).getStatus().equals("RUNNING")&&processes.get(i).getStation().equals(server.getStation())&&!processes.get(i).getName().equals(server.getName()))
				sos.add(processes.get(i));
		}
		return sos;
	}

	public static ArrayList<ServerObject> getWaitingProcesses(ServerObjectImpl server) {
		ArrayList<ServerObject> sos = new ArrayList<ServerObject>();
		synchronized(processesLock) {
			getAllProcesses();
			for (int i=0;i<processes.size();++i)
				if (processes.get(i).getStatus().equals("WAITING")&&processes.get(i).getStation().equals(server.getStation()))
					sos.add(processes.get(i));
		}
		return sos;
	}

	public static ArrayList<ServerObject> getAllWaiting() {
		ArrayList<ServerObject> sos = new ArrayList<ServerObject>();
		synchronized(processesLock) {
			getAllProcesses();
			for (int i=0;i<processes.size();++i)
				if (processes.get(i).getStatus().equals("WAITING"))
					sos.add(processes.get(i));
		}
		return sos;
	}

	private static ArrayList<ServerObject> getAllLeaders() {
		ArrayList<ServerObject> sos = new ArrayList<ServerObject>();
		synchronized(processesLock) {
			getAllProcesses();
			for (int i=0;i<processes.size();++i)
				if (processes.get(i).isLeader())
					sos.add(processes.get(i));
		}
		return sos;
	}

	public static ServerObject getLeader(ServerObjectImpl server) {
		synchronized(processesLock) {
			getAllProcesses();
			for (int i=0;i<processes.size();++i) {
				if (processes.get(i).isLeader()&&processes.get(i).getStation().equals(server.getStation()))
					return processes.get(i);
			}
		}
		return null;
	}

	public static boolean addProcess(ServerObjectImpl so) {
		try {
			if (ncRef==null)
				init();
			// get reference to rootpoa & activate the POAManager
			POA rootpoa = (POA)orb.resolve_initial_references("RootPOA");
			rootpoa.the_POAManager().activate();

			// register servant with the ORB
			// get object reference from the servant
			org.omg.CORBA.Object ref = rootpoa.servant_to_reference(so);
			// and cast the reference to a CORBA reference
			ServerObject href = ServerObjectHelper.narrow(ref);

			System.out.println(href.getName());

			// bind the Object Reference in Naming
			NameComponent path[] = ncRef.to_name(so.getName());
			ncRef.rebind(path, href);

			return true;
		}
		catch (Exception e) {

		}
		return false;
	}

	public static String getHostname() {
		return hostname;
	}

	public static void setNewHostname() {
		if (hostname==null) {
			try {
				hostname = java.net.InetAddress.getLocalHost().getHostName();
			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
			System.out.println("Initializing Naming Service at "+hostname);
		}
	}

	public static String printList(ArrayList<ServerObject> sos) {
		String returnString="";
		for (int i=0;i<sos.size();++i)
			returnString+=i+"] Name: "+sos.get(i).getName()+" Status: "+sos.get(i).getStatus()+"\n";
		return returnString;
	}

	public static void main(String[] args) {
		//CentralRepository.addProcess(new ServerObjectImpl(2016, 2017, 2019, "", true, ServerObjectImpl.Status.RUNNING, "SERVERNAME1", 6));
		//CentralRepository.addProcess(new ServerObjectImpl(2018, 2015, 2014, "", true, ServerObjectImpl.Status.RUNNING, "SERVERNAME2", 6));
		/*getAllProcesses();
		setNewHostname();
		System.out.println(processes.size());
		for (int i=0;i<processes.size();++i) {
			System.out.println(processes.get(i).getName());
			System.out.println(processes.get(i).getId());
		}
		ArrayList<ServerObject> sos2 = getAllLeaders();
		System.out.println(sos2.size());
		for (int i=0;i<sos2.size();++i) {
			System.out.println(sos2.get(i).getName());
			System.out.println(sos2.get(i).getId());
		}
		System.out.println(getLeader(new ServerObjectImpl(2016, 2017, 2019, "SPL", true, ServerObjectImpl.Status.RUNNING, "SERVERNAME1", 6, "Max")).getName());
		 */
		Scanner input = new Scanner(System.in);
		boolean showMenu=true;
		while (showMenu) {
			System.out.println("What would you like to do?\n"
					+ "1. View all registered processes\n"
					+ "2. View all leader replicas\n"
					+ "3. View all running replicas\n"
					+ "4. View all waiting processes\n"
					+ "5. Clear all naming service references\n"
					+ "6. Quit\n");
			int choice=Integer.parseInt(input.nextLine());
			switch(choice)
			{
			case 1:
				getAllProcesses();
				System.out.println(printList(processes));
				break;
			case 2:
				getAllProcesses();
				System.out.println(printList(getAllLeaders()));
				break;
			case 3:
				getAllProcesses();
				System.out.println(printList(getAllRunning()));
				break;
			case 4:
				getAllProcesses();
				System.out.println(printList(getAllWaiting()));
				break;
			case 5:
				clearBindings();
				System.out.println("Cleared Naming Service references");
				break;
			case 6:
				showMenu=false;
				break;
			default:
				System.out.println("Invalid Input, please try again.");
			}
		}
		System.exit(0);
	}

	public static void snapshot() {
		for (int i=0;i<processes.size();++i)
			list.add(new ServerObjectImpl(processes.get(i)));
	}

}
